package com.lge.remoteshot.camera;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.List;

import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Bitmap.Config;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.ImageFormat;
import android.graphics.Rect;
import android.graphics.YuvImage;
import android.hardware.Camera;
import android.hardware.Camera.Parameters;
import android.hardware.Camera.Size;
import android.util.AttributeSet;
import android.util.Log;
import android.view.SurfaceHolder;
import android.view.SurfaceView;

import com.lge.remoteshot.network.ClientManager;

public class CameraSurfaceView extends SurfaceView implements SurfaceHolder.Callback,
		Camera.PreviewCallback {
	private SurfaceHolder mHolder;
	private Camera mCamera;
	private ClientManager clientManager;

	private int mode;
	private byte[] frameBuffer;
	private boolean runCamera = false;

	public CameraSurfaceView(Context context) {
		// TODO Auto-generated constructor stub
		super(context);
	}

	public CameraSurfaceView(Context context, AttributeSet attrs) {
		super(context, attrs);
	}

	public void setMode(int mode) {
		this.mode = mode;
	}

	public void init() {
		mHolder = getHolder();
		mHolder.addCallback(this);
		mHolder.setFixedSize(getWidth(), getHeight());
	}

	public Camera getCamera() {
		return this.mCamera;
	}

	public void surfaceCreated(SurfaceHolder holder) {
		try {
			if (mode == CameraActivity.CAMERA_MODE_ORIGINAL) {
				mCamera = Camera.open();
				mCamera.setPreviewDisplay(mHolder);
				mCamera.setPreviewCallbackWithBuffer(this);
				mCamera.addCallbackBuffer(getFrameBuffer());
				mCamera.startPreview();
				runCamera = true;
				Log.d(CameraActivity.TAG, "CameraSurfaceView:surfaceCreated:MODE_ORIGINAL");
			} else if (mode == CameraActivity.CAMERA_MODE_REMOTE) {
				Log.d(CameraActivity.TAG, "CameraSurfaceView:surfaceCreated:MODE_REMOTE");
			}
		} catch (IOException e) {
			mCamera.release();
			mCamera = null;
		}

	}

	public void surfaceDestroyed(SurfaceHolder holder) {
		if (mCamera != null) {
			mCamera.stopPreview();
			mCamera.release();
			runCamera = false;
			mCamera = null;
		}
	}

	public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
		if (mode == CameraActivity.CAMERA_MODE_ORIGINAL) {
			mCamera.stopPreview();
			setPictureFormat(format);
			setPreviewSize(width, height);
			mCamera.setPreviewCallbackWithBuffer(this);
			mCamera.addCallbackBuffer(getFrameBuffer());
			mCamera.startPreview();
			runCamera = true;
		}
	}

	@Override
	public void onPreviewFrame(byte[] data, Camera camera) {
		// TODO Auto-generated method stub
		camera.addCallbackBuffer(data);
		if (mode != CameraActivity.CAMERA_MODE_ORIGINAL)
			return;
		if (data == null)
			return;
		if (clientManager == null)
			return;
		if (camera == null)
			return;

		if (runCamera) {
			// convert YUV to bitmap
			Size previewSize = camera.getParameters().getPreviewSize();
			YuvImage yuvimage = new YuvImage(data, ImageFormat.NV21, previewSize.width,
					previewSize.height, null);
			ByteArrayOutputStream baos = new ByteArrayOutputStream();
			yuvimage.compressToJpeg(new Rect(0, 0, previewSize.width, previewSize.height), 100,
					baos);
			Bitmap bitmap = BitmapFactory.decodeByteArray(baos.toByteArray(), 0,
					baos.toByteArray().length);
			Bitmap resizedBmp = Bitmap.createScaledBitmap(bitmap, previewSize.width / 8,
					previewSize.height / 8, true);
			baos = new ByteArrayOutputStream();
			resizedBmp.compress(Bitmap.CompressFormat.JPEG, 100, baos);
			// send data
			clientManager.sendFrameData(baos.toByteArray());

//			// convert YUV to bitmap
//			Size previewSize = camera.getParameters().getPreviewSize();
//			int[] argb = new int[previewSize.width * previewSize.height];
//			nv21ToRgb(argb, data, previewSize.width, previewSize.height);
//			Bitmap bitmap = Bitmap.createBitmap(argb, previewSize.width, previewSize.height, Config.ARGB_8888);
//			ByteArrayOutputStream baos = new ByteArrayOutputStream();
//			bitmap.compress(Bitmap.CompressFormat.JPEG, 100, baos);
//			// send data
//			clientManager.sendFrameData(baos.toByteArray());
		}
	}

	protected void setPictureFormat(int format) {
		Camera.Parameters params = mCamera.getParameters();
		params.setPictureSize(2048, 1536);
		params.setJpegQuality(100);
		List<Integer> supported = params.getSupportedPictureFormats();
		if (supported != null) {
			for (int f : supported) {
				if (f == format) {
					params.setPreviewFormat(format);
					Log.d(CameraActivity.TAG, "format : " + format);
					mCamera.setParameters(params);
					break;
				}
			}
		}
	}

	protected void setPreviewSize(int width, int height) {
		Camera.Parameters params = mCamera.getParameters();
		List<Camera.Size> supported = params.getSupportedPreviewSizes();
		if (supported != null) {
			for (Camera.Size size : supported) {
				if (size.width <= width && size.height <= height) {
					params.setPreviewSize(size.width, size.height);
					mCamera.setParameters(params);
					break;
				}
			}
		}
	}

	public byte[] getFrameBuffer() {
		Camera.Parameters tParams = mCamera.getParameters();
		int imgFormat = tParams.getPreviewFormat();
		int bitsPerPixel = ImageFormat.getBitsPerPixel(imgFormat);
		Camera.Size cameraSize = tParams.getPreviewSize();
		int frameSize = ((cameraSize.width * cameraSize.height) * bitsPerPixel) / 8;
		// debug
		Log.d(CameraActivity.TAG, "CameraSurfaceView:getFrameBuffer:frameSize:" + frameSize);
		frameBuffer = new byte[frameSize];
		return frameBuffer;
	}

	public void setClientManager(ClientManager manager) {
		this.clientManager = manager;
	}

	public void setReceiveFrame(final byte[] data) {
		new Thread(new Runnable() {

			@Override
			public void run() {
				// TODO Auto-generated method stub
				Canvas canvas = mHolder.lockCanvas();
				Bitmap bitmap = BitmapFactory.decodeByteArray(data, 0, data.length);
				if (bitmap != null)
					canvas.drawBitmap(bitmap, null, new Rect(0, 0, getWidth(), getHeight()), null);
				mHolder.unlockCanvasAndPost(canvas);
			}
		}).start();
	}

	public static void nv21ToRgb(int[] argb, byte[] yuv, int width, int height) {
		final int frameSize = width * height;

		final int ii = 0;
		final int ij = 0;
		final int di = +1;
		final int dj = +1;

		int a = 0;
		for (int i = 0, ci = ii; i < height; ++i, ci += di) {
			for (int j = 0, cj = ij; j < width; ++j, cj += dj) {
				int y = (0xff & ((int) yuv[ci * width + cj]));
				int v = (0xff & ((int) yuv[frameSize + (ci >> 1) * width + (cj & ~1) + 0]));
				int u = (0xff & ((int) yuv[frameSize + (ci >> 1) * width + (cj & ~1) + 1]));
				y = y < 16 ? 16 : y;

				int r = (int) (1.164f * (y - 16) + 1.596f * (v - 128));
				int g = (int) (1.164f * (y - 16) - 0.813f * (v - 128) - 0.391f * (u - 128));
				int b = (int) (1.164f * (y - 16) + 2.018f * (u - 128));

				r = r < 0 ? 0 : (r > 255 ? 255 : r);
				g = g < 0 ? 0 : (g > 255 ? 255 : g);
				b = b < 0 ? 0 : (b > 255 ? 255 : b);

				argb[a++] = 0xff000000 | (r << 16) | (g << 8) | b;
			}
		}
	}
}
/* end of file */
